﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Linq.Expressions;
using System.ComponentModel;
using System.Collections;

namespace Pavilion.DesignerTool.ViewTools
{
    /// <summary>
    /// Interaction logic for PropertyGird.xaml
    /// </summary>
    public partial class PropertyGird : UserControl, INotifyPropertyChanged
    {
        public static readonly DependencyProperty PropertiesSourceProperty = DependencyProperty.Register("PropertiesSource", typeof(object), typeof(PropertyGird),
            new PropertyMetadata(new PropertyChangedCallback(PropertiesSourceChanged)));
        public static readonly DependencyProperty CategoryContextMenuProperty = DependencyProperty.Register("CategoryContextMenu", typeof(ContextMenu), typeof(PropertyGird));
        public static readonly DependencyProperty PropertyContextMenuProperty = DependencyProperty.Register("PropertyContextMenu", typeof(ContextMenu), typeof(PropertyGird));
        public static readonly DependencyProperty AutoRemoveCatogoriesProperty = DependencyProperty.Register("AutoRemoveCatogories", typeof(bool), typeof(PropertyGird));
        
        public event PropertyChangedEventHandler PropertyChanged;

        public object PropertiesSource { get { return GetValue(PropertiesSourceProperty); } set { SetValue(PropertiesSourceProperty, value); } }
        public ContextMenu CategoryContextMenu { get { return (ContextMenu)GetValue(CategoryContextMenuProperty); } set { SetValue(CategoryContextMenuProperty, value); } }
        public ContextMenu PropertyContextMenu { get { return (ContextMenu)GetValue(PropertyContextMenuProperty); } set { SetValue(PropertyContextMenuProperty, value); } }
        public bool AutoRemoveCatogories { get { return (bool)GetValue(AutoRemoveCatogoriesProperty); } set { SetValue(AutoRemoveCatogoriesProperty, value); } }
        public IEnumerable<PropertyCategory> Categories { get { return categoriesDictionary.ValueCollection; } }
        public double TreeViewItemWidth { get { return treeViewItemWidth; } set { treeViewItemWidth = value; NotifyPropertyChangedBase.OnPropertyChanged<double>(PropertyChanged, this, () => TreeViewItemWidth); } }

        private double treeViewItemWidth;
        private object previousPropertiesSource;
        private NotifiableDictionary<object, PropertyCategory> categoriesDictionary;

        public PropertyGird()
        {
            categoriesDictionary = new NotifiableDictionary<object, PropertyCategory>();
            InitializeComponent();
        }

        public void RemoveProperty(PropertyItemBase item)
        {
            categoriesDictionary[item.AssociatedItem].RemoveProperty(item);

            if (AutoRemoveCatogories && categoriesDictionary[item.AssociatedItem].Properties.Count() == 0)
                RemoveCategory(item.AssociatedItem);
        }

        public void AddProperty(PropertyItemBase item)
        {
            if (!categoriesDictionary.ContainsKey(item.AssociatedItem))
                AddCategory(item.AssociatedItem);

            categoriesDictionary[item.AssociatedItem].AddProperty(item);
        }

        public void AddCategory(object associatedItem)
        {
            categoriesDictionary.Add(associatedItem, new PropertyCategory(associatedItem));
        }

        public void RemoveCategory(object associatedItem)
        {
            categoriesDictionary.Remove(associatedItem);
        }

        private void UpdatePropertiesSource()
        {
            categoriesDictionary.Clear();

            if (previousPropertiesSource is NotifiableCollection<PropertyItemBase>)
                UnregisterNotifiableCollection(previousPropertiesSource as NotifiableCollection<PropertyItemBase>);
            else if (previousPropertiesSource is NotifiableCollection<PropertyCategory>)
                UnregiesterPropertyCategory(previousPropertiesSource as NotifiableCollection<PropertyCategory>);

            previousPropertiesSource = PropertiesSource;

            if (PropertiesSource is NotifiableCollection<PropertyItemBase>)
                RegisterNotifiableCollection(PropertiesSource as NotifiableCollection<PropertyItemBase>);
            if (PropertiesSource is NotifiableCollection<PropertyCategory>)
                RegisterPropertyCategory(PropertiesSource as NotifiableCollection<PropertyCategory>);
            else if (PropertiesSource is IEnumerable)
                AddPropertiesOfCollection(PropertiesSource as IEnumerable);
            else
                AddProperitesOfItem(PropertiesSource);
        }

        private void RegisterPropertyCategory(NotifiableCollection<PropertyCategory> propertiesSource)
        {
            foreach (PropertyCategory category in propertiesSource)
                categoriesDictionary.Add(category.AssociatedItem, category);

            propertiesSource.Added += new Action<PropertyCategory, int>(CategoryCollection_Added);
            propertiesSource.Removed += new Action<PropertyCategory, int>(CategoryCollection_Removed);
            propertiesSource.Replaced += new ItemReplacedHandler<PropertyCategory>(CategoryCollection_Replaced);
        }

        private void UnregiesterPropertyCategory(NotifiableCollection<PropertyCategory> propertiesSource)
        {
            propertiesSource.Added -= new Action<PropertyCategory, int>(CategoryCollection_Added);
            propertiesSource.Removed -= new Action<PropertyCategory, int>(CategoryCollection_Removed);
            propertiesSource.Replaced -= new ItemReplacedHandler<PropertyCategory>(CategoryCollection_Replaced);
        }

        private void RegisterNotifiableCollection(NotifiableCollection<PropertyItemBase> propertiesSource)
        {
            propertiesSource.Added += new Action<PropertyItemBase, int>(PropertiesSource_Added);
            propertiesSource.Removed += new Action<PropertyItemBase, int>(PropertiesSource_Removed);
            propertiesSource.Replaced += new ItemReplacedHandler<PropertyItemBase>(PropertiesSource_Replaced);
        }

        private void UnregisterNotifiableCollection(NotifiableCollection<PropertyItemBase> propertiesSource)
        {
            if (propertiesSource != null)
            {
                propertiesSource.Added -= new Action<PropertyItemBase, int>(PropertiesSource_Added);
                propertiesSource.Removed -= new Action<PropertyItemBase, int>(PropertiesSource_Removed);
                propertiesSource.Replaced -= new ItemReplacedHandler<PropertyItemBase>(PropertiesSource_Replaced);
            }
        }

        private void AddPropertiesOfCollection(IEnumerable objectList)
        {
            if (objectList == null)
                return;

            foreach (object item in objectList)
                AddProperitesOfItem(item);
        }

        private void AddProperitesOfItem(object item)
        {
            if (PropertiesSource == null)
                return;

            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(item);

            for (int i = 0; i < properties.Count; i++)
                AddProperty((new ObjectPropertyItem(properties[i], item)));
        }

        private void PropertiesSource_Replaced(PropertyItemBase oldItem, PropertyItemBase newItem, int index)
        {
            RemoveProperty(oldItem);
            AddProperty(newItem);
        }

        private void PropertiesSource_Removed(PropertyItemBase item, int index)
        {
            RemoveProperty(item);
        }

        private void PropertiesSource_Added(PropertyItemBase item, int index)
        {
            AddProperty(item);
        }

        private void CategoryCollection_Replaced(PropertyCategory oldItem, PropertyCategory newItem, int index)
        {
            categoriesDictionary.Remove(oldItem.AssociatedItem);
            categoriesDictionary.Add(newItem.AssociatedItem, newItem);
        }

        private void CategoryCollection_Removed(PropertyCategory item, int index)
        {
            categoriesDictionary.Remove(item.AssociatedItem);
        }

        private void CategoryCollection_Added(PropertyCategory item, int index)
        {
            categoriesDictionary.Add(item.AssociatedItem, item);
        }

        static private void PropertiesSourceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            PropertyGird view = (PropertyGird)dependencyObject;

            view.UpdatePropertiesSource();
        }

        private void TreeView_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            TreeViewItemWidth = e.NewSize.Width / 2 - 22;
        }
    }
}
